﻿using System;
using System.Data;
using SlamCms.Common;

namespace SlamCms.Data
{
	public static class DbConnectionExtensions
	{
		/// <summary>
		/// Ensures the connection is open. If it's already open this method returns null. If it's closed this method returns an IDisposable to close the connection on Dispose. 
		/// </summary>
		/// <param name="connection">The connection you want to be open.</param>
		/// <returns>An IDiposable if the connection is closed, null if it's already open.</returns>
		/// <example>
		///		<code>
		///			using (connection.EnsureOpen()) {
		///				// Your code to work with the connection here.
		///			}
		///		</code>
		/// </example>
		public static IDisposable EnsureOpen(this IDbConnection connection)
		{
			Ensure.ArgumentNotNull(connection, "connection");
			
			switch (connection.State)
			{
				case ConnectionState.Open:
					return null;
				case ConnectionState.Closed:
					connection.Open();
					try
					{
						return new ConnectionCloser(connection);
					}
					catch
					{
						try { connection.Close(); }
						catch { } // we're already trying to handle, kthxbye
						throw;
					}

				default:
					throw new InvalidOperationException("Cannot use EnsureOpen when connection is " + connection.State);
			}
		}

		private class ConnectionCloser : IDisposable
		{
			private IDbConnection _connection;

			public ConnectionCloser(IDbConnection connection)
			{
				_connection = connection;
			}

			public void Dispose()
			{
				var cn = _connection;
				_connection = null;
				if (cn != null)
				{
					try { cn.Close(); }
					catch { }//throwing from Dispose() is so lame
				}
			}
		}
	}
}
